package com.xu.service;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Date;

/**
 * @Aspect 表示当前类是切面类
 */
@Component("Aspect")
@Aspect
public class MyAspectj {

    /**
     * @Before 前置通知
     * value 切入点表达式，表示切入的位置
     *
     */
//    @Before(value = "execution(public void com.xu.service.impl.SomeServiceImpl.doSome(..))")
    @Before(value = "execution(* * ..SomeServiceImpl.doSome(..))")
    public void before(){
        System.out.println("1==前置通知，在目标方法之前，执行输出日志时间"+new Date());
    }

    /**
     * 在目标方法执行之前执行。被注解为前置通知的方法，可以包含一个 JoinPoint 类型参
     * 数。该类型的对象本身就是切入点表达式。通过该参数，可获取切入点表达式、方法签名、
     * 目标对象等
     */
    @Before(value = "execution(* * ..SomeServiceImpl.doSome(..))")
    public void before02(JoinPoint jp){

        System.out.println("2==前置通知，在目标方法之前，执行输出日志时间"+new Date());
        System.out.println("连接点方法签名（定义）:"+jp.getSignature());
        System.out.println("连接点的方法参数个数:"+jp.getArgs().length);
        Object[] args = jp.getArgs();
        for (Object arg : args) {
            System.out.println("参数:"+arg);
        }
    }

    /**
     * @AfterReturning 后置通知
     * 返回值String是传值
     */
    @AfterReturning(value = "execution(* * ..SomeServiceImpl.doOther(..))",returning = "res")
    public void after01(Object res){
        System.out.println("3==后置通知，在目标方法之后,输出"+res);
        //根据返回值做一些操作
        if (res != null) {
            if("abcd".equals(res)){
                String s=(String) res;
                System.out.println(s.toUpperCase());
            }else {

            }
        }
    }

    /**
     * @AfterReturning 后置通知
     * 返回值对象是传引用
     */
    @AfterReturning(value = "execution(* * ..SomeServiceImpl.doStudent(..))",returning = "res")
    public void after02(Object res){
        System.out.println("3==后置通知，在目标方法之后,输出"+res);
        //根据返回值做一些操作
        if (res != null) {
            Student student=(Student) res;
            student.setName("hhh");
            student.setAge(20);

        }
    }

    /**
     * @Around()
     * 在目标方法执行之前之后执行。被注解为环绕增强的方法要有返回值，Object 类型。并
     * 且方法可以包含一个 ProceedingJoinPoint 类型的参数。接口 ProceedingJoinPoint 其有一个
     * proceed()方法，用于执行目标方法。若目标方法有返回值，则该方法的返回值就是目标方法
     * 的返回值。最后，环绕增强方法将其返回值返回。该增强方法实际是拦截了目标方法的执行。
     */
/*    @Around(value = "execution(* * ..SomeServiceImpl.First(..))")
    public Object aound(ProceedingJoinPoint pjd) throws Throwable {
        Object obj=null;
        //环绕通知，增强功能
        System.out.println("环绕通知，在目标方法执行前执行，例如输出日志");
        //相当于method.invoke(target,args)
        obj = pjd.proceed();
        System.out.println("环绕通知，在目标方法执行后执行，例如提交事务");
        System.out.println("若目标方法有返回值，则该方法proceed的返回值就是目标方法"+obj);
        return obj;
    }*/

    /**
     * 控制目标方法执行
     *
     */
    @Around(value = "execution(* * ..SomeServiceImpl.First(..))")
    public Object aound02(ProceedingJoinPoint pjd) throws Throwable {
        Object obj=null;
        //环绕通知，增强功能
        System.out.println("环绕通知，在目标方法执行前执行，例如输出日志");
        String name = (String) pjd.getArgs()[0];
        if("xsn".equals(name)){
            //相当于method.invoke(target,args)
            obj = pjd.proceed();
            //改变目标方法的执行结果
            obj="666";
        }
        System.out.println("环绕通知，在目标方法执行后执行，例如提交事务");
        System.out.println("若目标方法有返回值，则该方法proceed的返回值就是目标方法"+obj);
        return obj;
    }

    /**
     * @AfterThrowing 异常通知
     * 在目标方法抛出异常后执行。该注解的 throwing 属性用于指定所发生的异常类对象。
     * 当然，被注解为异常通知的方法可以包含一个参数 Throwable，参数名称为 throwing 指定的
     * 名称，表示发生的异常对象。
     */
    @AfterThrowing(value = "execution(* * ..SomeServiceImpl.doSecond(..))" ,throwing="ex")
    public void myAfterThrowing(Throwable ex){
        //可以把异常发生的时间，位置，原因记录到数据库，日志文件等
        //可以在异常发生时，把异常信息通过短信、邮件方式发送给开发人员

        System.out.println("异常通知：在执行目标方法时抛出异常，异常原因:"+ex.getMessage());
    }

    /**
     * @After  最终通知
     *
     */
    @After(value = "pointCut()")
    public void After(){
        System.out.println("最终通知：=====最后一定会被执行=====");
    }

    /**
     * @Pointcut 切入点表达式
     * 代码复用
     */
    @Pointcut(value = "execution(* * ..SomeServiceImpl.doFinally(..))")
    public void pointCut(){

    }

}
